<!--
   * 树形下拉选择组件
   * @author lakeside
   * @date 2019-11-04
   * 调用示例：
   * <tree-select :height="400" // 下拉框中树形高度
   *        :width="200" // 下拉框中树形宽度
   *        size="small"  // 输入框的尺寸: medium/small/mini
   *        :nodeData="data" // 树结构的数据
   *        :nodeProps="props" // 树结构的props
   *        :nodeKey="nodeKey"   // 绑定nodeKey，默认绑定'id'
   *        multiple   // 多选
   *        clearable   // 可清空选择
   *        collapseTags   // 多选时将选中值按文字的形式展示
   *        checkStrictly // 多选时，严格遵循父子不互相关联
   *        :defaultEntry="defaultEntry"  // 传递默认选中的节点key组成的数组
   *        @changed="change"> // 选择完成后的回调，事件有两个参数：第一个是所有选中的节点ID，第二个是所有选中的节点数据
   *        @selectFilter=function //节点选择时的过滤器，返回为false时取消当前选择
   *        </tree-select> @current-change="handleCurrentChange"
-->
<template>
  <el-popover v-model="popoverShow" v-click-outside="()=>{popoverShow=false}" :width="width" placement="bottom-start"
              trigger="manual" @hide="selectedChange">
    <el-input v-model="filterText" class="filter" placeholder="输入关键字进行过滤"></el-input>
    <el-tree ref="tree" :check-on-click-node="multiple" :check-strictly="checkStrictly" :data="treeData" :default-expand-all="expandAll"
             :expand-on-click-node="false"
             :filter-node-method="filterNode"
             :highlight-current="true"
             :node-key="nodeKey"
             :props="nodeProps"
             :show-checkbox="multiple"
             :style="treeStyle"
             class="tree"
             @current-change="handleCurrentChange"
             @check-change="handleCheckChange">
      <span slot-scope="{ node, data }" class="tree_node_toolbar">
        <template>
          <span>{{ node.label }}
          <span v-for="(tag,i) in nodeProps.tags">
            <span class="span_tag">{{ data[tag] }}</span>
          </span>
          </span>
        </template>
      </span>
    </el-tree>
    <el-select ref="select" slot="reference" v-model="selectedKey" :clearable="clearable"
               :collapse-tags="collapseTags"
               :disabled="disabled"
               :multiple="multiple"
               :placeholder="placeholder"
               :size="size"
               @clear="clearSelected"
               @click.native="popoverShow = !popoverShow&&!disabled"
               @remove-tag="removeSelectedTag">
      <el-option v-for="item in selectedEntry" :key="item[nodeKey]" :label="item[nodeProps.label]"
                 :value="item[nodeKey]"></el-option>
    </el-select>
  </el-popover>

</template>

<script>
import lodash from 'lodash/core'

let isString = val => typeof val === 'string'
let hasString = val => Object.prototype.toString.call(val) === '[object String]'
export default {
  name: 'tree-select',
  model: {
    prop: 'defaultData',//prop:将父组件中v-model绑定的数据赋值给defaultNode(注意defaultNode只能为props中的key)
    event: 'selected' //event:使用this.$emit('selected')将数据返回给父组件
  },
  props: {
    placeholder: {type: String, default: ''},
    //弹出层宽高
    height: {type: null, default: '300px'},
    width: {type: null, default: 'auto'},
    //是否禁用
    disabled: {type: Boolean, default: false},
    // 配置是否可多选
    multiple: {type: Boolean, default: false},
    // 配置是否可清空选择
    clearable: {type: Boolean, default: true},
    // 配置多选时是否将选中值按文字的形式展示
    collapseTags: {type: Boolean, default: true},
    size: {type: String, default: ''},
    // tree结构数据
    nodeData: {type: Array, default:()=>[]},
    //tree的属性配置
    nodeProps: {
      type: Object, default() {
        return {children: 'children', label: 'name', tags: []}
      }
    },
    //tree的key字段
    nodeKey: {type: String, default: 'id'},
    //tree展开数据
    expandAll: {type: Boolean, default: true},
    //tree默认数据
    defaultData: {type: String | Object | Array, default: undefined},
    //tree多选时返回是否包含半选框数据
    containHalf: {type: Boolean, default: false},
    //tree显示复选框情况下，是否严格遵循父子不互相关联
    checkStrictly: {type: Boolean, default: false},
    //tree选择时的过滤器
    selectFilter: {type: Function, default: (data, node) => true},//!data.children
    dataMode: {type: String, default: 'String'},
  },
  data() {
    return {
      // 默认节点
      defaultKey: null,
      //当前节点
      currentKey: '',
      //defaultEntry: null,
      //dataMode:'String',
      popoverShow: false, // 是否显示树状选择器
      selectedKey: null, // select中value必须绑定一个基于string的数字
      selectedEntry: [], // 选中的实体,必须是一个对象数组
      treeStyle: 'width:' + this.width + ';' + 'height:' + this.height + ';',
      filterText: '',
      treeData: [] // 树加载的数据
    };
  },
  methods: {
    //tree过滤
    filterNode(value, data) {
      if (!value) return true;
      return data[this.nodeProps.label].indexOf(value) !== -1;
    },
    //清空所有勾选
    clearSelected() {
      this.multiple ? this.$refs.tree.setCheckedKeys([]) : this.$refs.tree.setCurrentKey(null)
      this.selectedChange()
    },

    //删除选择标识
    removeSelectedTag(val) {
      this.$refs.tree.setChecked(val, false)
      this.selectedChange()
    },

    //设置选中key
    initDefaultSelected() {
      let treeObj = this.$refs.tree
      if (this.multiple) {
        treeObj.setCheckedKeys([]);
        if (this.defaultKey) {
          if (this.defaultKey instanceof Array && this.defaultKey.length > 0) {
            //优化半选节点选中设置
            this.defaultKey.map(key => {
              treeObj.setChecked(key, true, false)
            })
          } else {
            treeObj.setChecked(this.defaultKey, true, false)
          }
        }
      } else {
        treeObj.setCurrentKey(this.defaultKey || null)
      }
      this.currentSelected()
    },
    /**
     * 提取选中节点
     */
    currentSelected() {
      let treeObj = this.$refs.tree
      this.selectedEntry = []
      if (this.multiple) {
        this.selectedKey = []
        let checkedNodes = treeObj.getCheckedNodes(false, this.containHalf)
        if (checkedNodes) {
          this.selectedEntry = checkedNodes
          this.selectedKey = checkedNodes.map(node => node[this.nodeKey])
        }
      } else {
        this.selectedKey = ''
        let currNode = treeObj.getCurrentNode()
        if (currNode) {
          this.selectedEntry.push(currNode)
          this.selectedKey = treeObj.getCurrentKey()
        }
      }
    },
    /**
     * 回调选择改变
     */
    selectedChange() {
      this.currentSelected()
      if (this.dataMode == 'Object') {
        this.$emit('selected', this.selectedEntry)
      } else {
        this.$emit('selected', this.selectedKey)
      }
      this.$emit('changed', this.selectedKey, this.selectedEntry)
    },

    /**
     * 单选时
     * 增加过滤
     */
    handleCurrentChange(data, node) {
      if (!this.multiple) {
        let bool = this.selectFilter(data, node)
        if (bool && !this.multiple) {
          this.popoverShow = false
        } else {
          // 取消选中
          this.$refs.tree.setCurrentKey(null)
          //lodash.remove(this.selectedKey, n => n == data[this.nodeKey])
        }
      }
    },
    /**
     * 多选选中时
     * 增加过滤
     */
    handleCheckChange(data, isCheck) {
      let node = this.$refs.tree.getNode(data)
      // 当前是选中状态
      if (isCheck && !this.selectFilter(data, node)) {
        this.$refs.tree.setChecked(data, false)
        //lodash.remove(this.selectedKey, n => n == data[this.nodeKey])
      }
    },
    /**
     * 处理默认值
     * 支持传主键数组或者对象数组
     */
    handlerDefault() {
      this.defaultKey = null
      this.currentKey = null
      if (this.defaultData) {
        if (this.defaultData instanceof Array) {
          if (this.defaultData.length > 0) {
            if (hasString(this.defaultData[0])) {
              this.defaultKey = this.defaultData
            } else {
              this.defaultKey = this.defaultData.map(entry => entry[this.nodeKey])
            }
          }
        } else if (hasString(this.defaultData)) {
          this.defaultKey = this.defaultData
          this.currentKey = this.defaultKey
        } else {
          if (this.defaultData) {
            this.defaultKey = this.defaultData[this.nodeKey]
            this.currentKey = this.defaultKey
          }
        }
      }
      if (!lodash.isEqual(this.defaultKey, this.selectedKey)) {
        this.initDefaultSelected()
      }
    }
  },
  watch: {
    popoverShow() {
      // 隐藏select自带的下拉框
      this.$refs.select.blur()
    },
    defaultData: {
      handler(newVal, oldVal) {
        this.handlerDefault()
      },
      //在watch中首次绑定的时候，执行handler
      immediate: true
    },
    filterText(val) {
      this.$refs.tree.filter(val)
    },
    nodeData: {
      handler(newVal, oldVal) {
        this.treeData = this.nodeData || []
      },
      //在watch中首次绑定的时候，执行handler
      immediate: true
    }
  }
}
</script>

<style lang="scss" scoped>
.popover {
  width: 100%;
  height: 100%;
  position: fixed;
  top: 0;
  left: 0;
  opacity: 0;
}

.tree {
  overflow: auto;
}

.filter {
  margin-bottom: 10px;
}

</style>
